Function declaration seems to work

This commit is contained in:
2022-08-25 15:19:08 +01:00
parent 4f70bea808
commit 041fe99e01
13 changed files with 615 additions and 93 deletions

View File

@@ -25,6 +25,7 @@ TODO: functions are invoked by calling thier names
TODO: functions are first-class citizens TODO: functions are first-class citizens
TODO: functions last argument can be a rest parameter TODO: functions last argument can be a rest parameter
TODO: Nullish types
TODO: A way to check the type of a variable (typeOf keyword) TODO: A way to check the type of a variable (typeOf keyword)
TODO: a = b = c = 1; ? TODO: a = b = c = 1; ?
TODO: are compounds shallow or deep copies? TODO: are compounds shallow or deep copies?

9
scripts/function.toy Normal file
View File

@@ -0,0 +1,9 @@
fn name(argument: type, arg2: type, ...rest): int {
print "hello world";
return 0;
}
print name;

View File

@@ -21,6 +21,36 @@ void initCompiler(Compiler* compiler) {
} }
//separated out, so it can be recursive //separated out, so it can be recursive
static int writeLiteralTypeToCache(LiteralArray* literalCache, Literal literal) {
//if it's a compound type, recurse and store the results
if (AS_TYPE(literal).typeOf == LITERAL_ARRAY || AS_TYPE(literal).typeOf == LITERAL_DICTIONARY) {
//I don't like storing types in an array, but it's the easiest and most straight forward method
LiteralArray* store = ALLOCATE(LiteralArray, 1);
initLiteralArray(store);
//store the base literal in the store
pushLiteralArray(store, literal);
for (int i = 0; i < AS_TYPE(literal).count; i++) {
//write the values to the cache, and the indexes to the store
int subIndex = writeLiteralTypeToCache(literalCache, ((Literal*)(AS_TYPE(literal).subtypes))[i]);
pushLiteralArray(store, TO_INTEGER_LITERAL(subIndex));
}
//push the store to the cache, tweaking the type
literal = TO_ARRAY_LITERAL(store);
literal.type = LITERAL_TYPE_INTERMEDIATE; //NOTE: tweaking the type usually isn't a good idea
}
//BUGFIX: check if exactly this literal array exists
int index = findLiteralIndex(literalCache, literal);
if (index < 0) {
index = pushLiteralArray(literalCache, literal);
}
return index;
}
static int writeNodeCompoundToCache(Compiler* compiler, Node* node) { static int writeNodeCompoundToCache(Compiler* compiler, Node* node) {
int index = -1; int index = -1;
@@ -124,34 +154,61 @@ static int writeNodeCompoundToCache(Compiler* compiler, Node* node) {
return index; return index;
} }
static int writeLiteralTypeToCache(LiteralArray* literalCache, Literal literal) { static int writeNodeCollectionToCache(Compiler* compiler, Node* node) {
//if it's a compound type, recurse and store the results //stored as an array
if (AS_TYPE(literal).typeOf == LITERAL_ARRAY || AS_TYPE(literal).typeOf == LITERAL_DICTIONARY) { LiteralArray* store = ALLOCATE(LiteralArray, 1);
//I don't like storing types in an array, but it's the easiest and most straight forward method initLiteralArray(store);
LiteralArray* store = ALLOCATE(LiteralArray, 1);
initLiteralArray(store);
//store the base literal in the store //ensure each literal value is in the cache, individually
pushLiteralArray(store, literal); for (int i = 0; i < node->fnCollection.count; i++) {
switch(node->fnCollection.nodes[i].type) {
case NODE_VAR_DECL: {
//write each piece of the declaration to the bytecode
int identifierIndex = findLiteralIndex(&compiler->literalCache, node->fnCollection.nodes[i].varDecl.identifier);
if (identifierIndex < 0) {
identifierIndex = pushLiteralArray(&compiler->literalCache, node->fnCollection.nodes[i].varDecl.identifier);
}
for (int i = 0; i < AS_TYPE(literal).count; i++) { int typeIndex = writeLiteralTypeToCache(&compiler->literalCache, node->fnCollection.nodes[i].varDecl.typeLiteral);
//write the values to the cache, and the indexes to the store
int subIndex = writeLiteralTypeToCache(literalCache, ((Literal*)(AS_TYPE(literal).subtypes))[i]); //embed the info into the bytecode
pushLiteralArray(store, TO_INTEGER_LITERAL(subIndex)); if (identifierIndex >= 256 || typeIndex >= 256) {
//push a "long" declaration
compiler->bytecode[compiler->count++] = OP_VAR_DECL_LONG; //1 byte
*((unsigned short*)(compiler->bytecode + compiler->count)) = (unsigned short)identifierIndex; //2 bytes
compiler->count += sizeof(unsigned short);
*((unsigned short*)(compiler->bytecode + compiler->count)) = (unsigned short)typeIndex; //2 bytes
compiler->count += sizeof(unsigned short);
}
else {
//push a declaration
compiler->bytecode[compiler->count++] = OP_VAR_DECL; //1 byte
compiler->bytecode[compiler->count++] = (unsigned char)identifierIndex; //1 byte
compiler->bytecode[compiler->count++] = (unsigned char)typeIndex; //1 byte
}
}
break;
case NODE_LITERAL: {
//values
int val = findLiteralIndex(&compiler->literalCache, node->fnCollection.nodes[i].atomic.literal);
if (val < 0) {
val = pushLiteralArray(&compiler->literalCache, node->fnCollection.nodes[i].atomic.literal);
}
pushLiteralArray(store, TO_INTEGER_LITERAL(val));
}
break;
default:
fprintf(stderr, ERROR "[internal] Unrecognized node type in writeNodeCollectionToCache()" RESET);
return -1;
} }
//push the store to the cache, tweaking the type
literal = TO_ARRAY_LITERAL(store);
literal.type = LITERAL_TYPE_INTERMEDIATE; //NOTE: tweaking the type usually isn't a good idea
} }
//BUGFIX: check if exactly this literal array exists //push the store to the cache, with instructions about how pack it
int index = findLiteralIndex(literalCache, literal); return pushLiteralArray(&compiler->literalCache, TO_ARRAY_LITERAL(store));
if (index < 0) {
index = pushLiteralArray(literalCache, literal);
}
return index;
} }
static int writeLiteralToCompiler(Compiler* compiler, Literal literal) { static int writeLiteralToCompiler(Compiler* compiler, Literal literal) {
@@ -310,6 +367,66 @@ static void writeCompilerWithJumps(Compiler* compiler, Node* node, void* breakAd
} }
break; break;
case NODE_FN_DECL: {
//run a compiler over the function
Compiler* fnCompiler = ALLOCATE(Compiler, 1);
initCompiler(fnCompiler);
writeCompiler(fnCompiler, node->fnDecl.arguments);
writeCompiler(fnCompiler, node->fnDecl.returns);
writeCompiler(fnCompiler, node->fnDecl.block);
//create the function in the literal cache (by storing the compiler object)
Literal fnLiteral = TO_FUNCTION_LITERAL(fnCompiler, 0);
fnLiteral.type = LITERAL_FUNCTION_INTERMEDIATE; //NOTE: changing type
//push the name
int identifierIndex = findLiteralIndex(&compiler->literalCache, node->fnDecl.identifier);
if (identifierIndex < 0) {
identifierIndex = pushLiteralArray(&compiler->literalCache, node->fnDecl.identifier);
}
//push to function (functions are never equal)
int fnIndex = pushLiteralArray(&compiler->literalCache, fnLiteral);
//embed the info into the bytecode
if (identifierIndex >= 256 || fnIndex >= 256) {
//push a "long" declaration
compiler->bytecode[compiler->count++] = OP_FN_DECL_LONG; //1 byte
*((unsigned short*)(compiler->bytecode + compiler->count)) = (unsigned short)identifierIndex; //2 bytes
compiler->count += sizeof(unsigned short);
*((unsigned short*)(compiler->bytecode + compiler->count)) = (unsigned short)fnIndex; //2 bytes
compiler->count += sizeof(unsigned short);
}
else {
//push a declaration
compiler->bytecode[compiler->count++] = OP_FN_DECL; //1 byte
compiler->bytecode[compiler->count++] = (unsigned char)identifierIndex; //1 byte
compiler->bytecode[compiler->count++] = (unsigned char)fnIndex; //1 byte
}
}
break;
case NODE_FN_COLLECTION: {
int index = writeNodeCollectionToCache(compiler, node);
//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_PATH_IF: { case NODE_PATH_IF: {
//process the condition //process the condition
writeCompilerWithJumps(compiler, node->path.condition, breakAddressesPtr, continueAddressesPtr); writeCompilerWithJumps(compiler, node->path.condition, breakAddressesPtr, continueAddressesPtr);
@@ -481,6 +598,20 @@ static void writeCompilerWithJumps(Compiler* compiler, Node* node, void* breakAd
} }
break; break;
case NODE_PATH_RETURN: {
//read each returned literal onto the stack, and return the number of values to return
for (int i = 0; i < node->path.thenPath->fnCollection.count; i++) {
writeCompilerWithJumps(compiler, &node->path.thenPath->fnCollection.nodes[i], breakAddressesPtr, continueAddressesPtr);
}
//push the return, with the number of literals
compiler->bytecode[compiler->count++] = OP_RETURN; //1 byte
*((unsigned short*)(compiler->bytecode + compiler->count)) = (unsigned short)(node->path.thenPath->fnCollection.count); //2 bytes
compiler->count += sizeof(unsigned short);
}
break;
case NODE_INCREMENT_PREFIX: { case NODE_INCREMENT_PREFIX: {
//push the literal to the stack (twice) //push the literal to the stack (twice)
writeLiteralToCompiler(compiler, node->increment.identifier); writeLiteralToCompiler(compiler, node->increment.identifier);
@@ -582,29 +713,37 @@ static void emitFloat(unsigned char** collationPtr, int* capacityPtr, int* count
} }
//return the result //return the result
unsigned char* collateCompiler(Compiler* compiler, int* size) { static unsigned char* collateCompilerHeaderOpt(Compiler* compiler, int* size, bool embedHeader) {
int capacity = GROW_CAPACITY(0); int capacity = GROW_CAPACITY(0);
int count = 0; int count = 0;
unsigned char* collation = ALLOCATE(unsigned char, capacity); unsigned char* collation = ALLOCATE(unsigned char, capacity);
//embed the header with version information //for the function-section at the end of the main-collation
emitByte(&collation, &capacity, &count, TOY_VERSION_MAJOR); int fnIndex = 0; //counts up for each fn
emitByte(&collation, &capacity, &count, TOY_VERSION_MINOR); int fnCapacity = GROW_CAPACITY(0);
emitByte(&collation, &capacity, &count, TOY_VERSION_PATCH); int fnCount = 0;
unsigned char* fnCollation = ALLOCATE(unsigned char, fnCapacity);
//embed the build info if (embedHeader) {
if (strlen(TOY_VERSION_BUILD) + count + 1 > capacity) { //embed the header with version information
int oldCapacity = capacity; emitByte(&collation, &capacity, &count, TOY_VERSION_MAJOR);
capacity = strlen(TOY_VERSION_BUILD) + count + 1; //full header size emitByte(&collation, &capacity, &count, TOY_VERSION_MINOR);
collation = GROW_ARRAY(unsigned char, collation, oldCapacity, capacity); emitByte(&collation, &capacity, &count, TOY_VERSION_PATCH);
//embed the build info
if (strlen(TOY_VERSION_BUILD) + count + 1 > capacity) {
int oldCapacity = capacity;
capacity = strlen(TOY_VERSION_BUILD) + count + 1; //full header size
collation = GROW_ARRAY(unsigned char, collation, oldCapacity, capacity);
}
memcpy(&collation[count], TOY_VERSION_BUILD, strlen(TOY_VERSION_BUILD));
count += strlen(TOY_VERSION_BUILD);
collation[count++] = '\0'; //terminate the build string
emitByte(&collation, &capacity, &count, OP_SECTION_END); //terminate header
} }
memcpy(&collation[count], TOY_VERSION_BUILD, strlen(TOY_VERSION_BUILD));
count += strlen(TOY_VERSION_BUILD);
collation[count++] = '\0'; //terminate the build string
emitByte(&collation, &capacity, &count, OP_SECTION_END); //terminate header
//embed the data section (first short is the number of literals) //embed the data section (first short is the number of literals)
emitShort(&collation, &capacity, &count, compiler->literalCache.count); emitShort(&collation, &capacity, &count, compiler->literalCache.count);
@@ -684,7 +823,33 @@ unsigned char* collateCompiler(Compiler* compiler, int* size) {
} }
break; break;
//TODO: function case LITERAL_FUNCTION_INTERMEDIATE: {
//extract the compiler
Literal fn = compiler->literalCache.literals[i];
void* fnCompiler = AS_FUNCTION(fn);
//collate the function into bytecode (without header)
int size = 0;
unsigned char* bytes = collateCompilerHeaderOpt((Compiler*)fnCompiler, &size, false);
//emit how long this section is, +1 for ending mark
emitShort(&fnCollation, &fnCapacity, &fnCount, (unsigned short)size + 1);
//write the fn to the fn collation
for (int i = 0; i < size; i++) {
emitByte(&fnCollation, &fnCapacity, &fnCount, bytes[i]);
}
emitByte(&fnCollation, &fnCapacity, &fnCount, OP_FN_END); //for marking the correct end-point of the function
//embed the reference to the function implementation into the current collation (to be extracted later)
emitByte(&collation, &capacity, &count, LITERAL_FUNCTION);
emitShort(&collation, &capacity, &count, (unsigned short)(fnIndex++));
freeCompiler((Compiler*)fnCompiler);
FREE(Compiler, fnCompiler);
}
break;
case LITERAL_IDENTIFIER: { case LITERAL_IDENTIFIER: {
emitByte(&collation, &capacity, &count, LITERAL_IDENTIFIER); emitByte(&collation, &capacity, &count, LITERAL_IDENTIFIER);
@@ -744,6 +909,18 @@ unsigned char* collateCompiler(Compiler* compiler, int* size) {
emitByte(&collation, &capacity, &count, OP_SECTION_END); //terminate data emitByte(&collation, &capacity, &count, OP_SECTION_END); //terminate data
//embed the function section (beginning with function count, size)
emitShort(&collation, &capacity, &count, fnIndex);
emitShort(&collation, &capacity, &count, fnCount);
for (int i = 0; i < fnCount; i++) {
emitByte(&collation, &capacity, &count, fnCollation[i]);
}
emitByte(&collation, &capacity, &count, OP_SECTION_END); //terminate function section
FREE_ARRAY(unsigned char, fnCollation, fnCapacity); //clear the function stuff
//code section //code section
for (int i = 0; i < compiler->count; i++) { for (int i = 0; i < compiler->count; i++) {
emitByte(&collation, &capacity, &count, compiler->bytecode[i]); emitByte(&collation, &capacity, &count, compiler->bytecode[i]);
@@ -760,3 +937,7 @@ unsigned char* collateCompiler(Compiler* compiler, int* size) {
return collation; return collation;
} }
unsigned char* collateCompiler(Compiler* compiler, int* size) {
return collateCompilerHeaderOpt(compiler, size, true);
}

View File

@@ -44,6 +44,10 @@ void freeInterpreter(Interpreter* interpreter) {
interpreter->literalCache.literals[i] = TO_NULL_LITERAL; interpreter->literalCache.literals[i] = TO_NULL_LITERAL;
} }
if (IS_FUNCTION(interpreter->literalCache.literals[i])) {
FREE_ARRAY(unsigned char, interpreter->literalCache.literals[i].as.function.ptr, interpreter->literalCache.literals[i].as.function.length);
}
} }
freeLiteralArray(&interpreter->literalCache); freeLiteralArray(&interpreter->literalCache);
@@ -874,41 +878,8 @@ static void execInterpreter(Interpreter* interpreter) {
} }
} }
void runInterpreter(Interpreter* interpreter, unsigned char* bytecode, int length) {
//prep the bytecode
interpreter->bytecode = bytecode;
interpreter->length = length;
interpreter->count = 0;
if (!interpreter->bytecode) {
printf(ERROR "Error: No valid bytecode given\n" RESET);
return;
}
//prep the literal cache
if (interpreter->literalCache.count > 0) {
freeLiteralArray(&interpreter->literalCache); //automatically inits
}
//header section
const unsigned char major = readByte(interpreter->bytecode, &interpreter->count);
const unsigned char minor = readByte(interpreter->bytecode, &interpreter->count);
const unsigned char patch = readByte(interpreter->bytecode, &interpreter->count);
if (major != TOY_VERSION_MAJOR || minor != TOY_VERSION_MINOR || patch != TOY_VERSION_PATCH) {
printf(ERROR "Error: interpreter/bytecode version mismatch\n" RESET);
}
const char* build = readString(interpreter->bytecode, &interpreter->count);
if (command.verbose) {
if (strncmp(build, TOY_VERSION_BUILD, strlen(TOY_VERSION_BUILD))) {
printf(WARN "Warning: interpreter/bytecode build mismatch\n" RESET);
}
}
consumeByte(OP_SECTION_END, interpreter->bytecode, &interpreter->count);
static void readInterpreterSections(Interpreter* interpreter) {
//data section //data section
const short literalCount = readShort(interpreter->bytecode, &interpreter->count); const short literalCount = readShort(interpreter->bytecode, &interpreter->count);
@@ -1017,7 +988,22 @@ void runInterpreter(Interpreter* interpreter, unsigned char* bytecode, int lengt
} }
break; break;
//TODO: functions case LITERAL_FUNCTION: {
//read the index
unsigned short index = readShort(interpreter->bytecode, &interpreter->count);
Literal literal = TO_INTEGER_LITERAL(index);
//change the type, to read it PROPERLY below
literal.type = LITERAL_FUNCTION_INTERMEDIATE;
//push to the literal cache
pushLiteralArray(&interpreter->literalCache, literal);
if (command.verbose) {
printf("(function)\n");
}
}
break;
case LITERAL_IDENTIFIER: { case LITERAL_IDENTIFIER: {
char* str = readString(interpreter->bytecode, &interpreter->count); char* str = readString(interpreter->bytecode, &interpreter->count);
@@ -1085,16 +1071,85 @@ void runInterpreter(Interpreter* interpreter, unsigned char* bytecode, int lengt
} }
} }
consumeByte(OP_SECTION_END, interpreter->bytecode, &interpreter->count); consumeByte(OP_SECTION_END, interpreter->bytecode, &interpreter->count); //terminate the literal section
//read the function metadata
int functionCount = readShort(interpreter->bytecode, &interpreter->count);
int functionSize = readShort(interpreter->bytecode, &interpreter->count); //might not be needed
//read in the functions
for (int i = 0; i < interpreter->literalCache.count; i++) {
if (interpreter->literalCache.literals[i].type == LITERAL_FUNCTION_INTERMEDIATE) {
//get the size of the function
size_t size = (size_t)readShort(interpreter->bytecode, &interpreter->count);
//read the function code (literal cache and all)
unsigned char* bytes = ALLOCATE(unsigned char, size);
memcpy(bytes, interpreter->bytecode + interpreter->count, size);
interpreter->count += size;
//assert that the last memory slot is function end
if (bytes[size - 1] != OP_FN_END) {
printf(ERROR "[internal] Failed to find function end" RESET);
FREE_ARRAY(unsigned char, bytes, size);
return;
}
//change the type to normal
interpreter->literalCache.literals[i] = TO_FUNCTION_LITERAL(bytes, size);
}
}
//TODO
//set the starting point for the interpreter //set the starting point for the interpreter
interpreter->codeStart = interpreter->count; interpreter->codeStart = interpreter->count;
}
void runInterpreter(Interpreter* interpreter, unsigned char* bytecode, int length) {
//prep the bytecode
interpreter->bytecode = bytecode;
interpreter->length = length;
interpreter->count = 0;
if (!interpreter->bytecode) {
printf(ERROR "Error: No valid bytecode given\n" RESET);
return;
}
//prep the literal cache
if (interpreter->literalCache.count > 0) {
freeLiteralArray(&interpreter->literalCache); //automatically inits
}
//header section
const unsigned char major = readByte(interpreter->bytecode, &interpreter->count);
const unsigned char minor = readByte(interpreter->bytecode, &interpreter->count);
const unsigned char patch = readByte(interpreter->bytecode, &interpreter->count);
if (major != TOY_VERSION_MAJOR || minor != TOY_VERSION_MINOR || patch != TOY_VERSION_PATCH) {
printf(ERROR "Error: interpreter/bytecode version mismatch\n" RESET);
}
const char* build = readString(interpreter->bytecode, &interpreter->count);
if (command.verbose) {
if (strncmp(build, TOY_VERSION_BUILD, strlen(TOY_VERSION_BUILD))) {
printf(WARN "Warning: interpreter/bytecode build mismatch\n" RESET);
}
}
consumeByte(OP_SECTION_END, interpreter->bytecode, &interpreter->count);
//read the sections of the bytecode
readInterpreterSections(interpreter);
//code section //code section
if (command.verbose) { if (command.verbose) {
printf(NOTICE "executing bytecode\n" RESET); printf(NOTICE "executing bytecode\n" RESET);
} }
//execute the interpreter
execInterpreter(interpreter); execInterpreter(interpreter);
//free the bytecode immediately after use //free the bytecode immediately after use

View File

@@ -14,7 +14,7 @@ typedef struct Interpreter {
unsigned char* bytecode; unsigned char* bytecode;
int length; int length;
int count; int count;
int codeStart; int codeStart; //for jumps
LiteralArray literalCache; //read-only - built from the bytecode, refreshed each time new bytecode is provided LiteralArray literalCache; //read-only - built from the bytecode, refreshed each time new bytecode is provided
//operation //operation

View File

@@ -284,7 +284,9 @@ Token scanLexer(Lexer* lexer) {
case ';': return makeToken(lexer, TOKEN_SEMICOLON); case ';': return makeToken(lexer, TOKEN_SEMICOLON);
case ',': return makeToken(lexer, TOKEN_COMMA); case ',': return makeToken(lexer, TOKEN_COMMA);
case '.': case '.':
if (peek(lexer) == '.' && peekNext(lexer) == ',') { if (peek(lexer) == '.' && peekNext(lexer) == '.') {
advance(lexer);
advance(lexer);
return makeToken(lexer, TOKEN_REST); return makeToken(lexer, TOKEN_REST);
} }
return makeToken(lexer, TOKEN_DOT); return makeToken(lexer, TOKEN_DOT);

View File

@@ -187,6 +187,10 @@ void printLiteralCustom(Literal literal, void (printFn)(const char*)) {
break; break;
//TODO: functions //TODO: functions
case LITERAL_FUNCTION: {
printFn("(function)");
}
break;
case LITERAL_IDENTIFIER: { case LITERAL_IDENTIFIER: {
char buffer[256]; char buffer[256];
@@ -293,9 +297,14 @@ void printLiteralCustom(Literal literal, void (printFn)(const char*)) {
} }
break; break;
default: case LITERAL_TYPE_INTERMEDIATE:
//should never bee seen case LITERAL_FUNCTION_INTERMEDIATE:
fprintf(stderr, ERROR "[internal] Unrecognized literal type in print: %d\n" RESET, literal.type); printFn("Unprintable literal found");
break;
case LITERAL_ANY:
printFn("(any)");
break;
} }
} }
@@ -438,7 +447,9 @@ bool literalsAreEqual(Literal lhs, Literal rhs) {
return true; return true;
//TODO: functions case LITERAL_FUNCTION:
return false; //functions are never equal
break;
case LITERAL_IDENTIFIER: case LITERAL_IDENTIFIER:
//check shortcuts //check shortcuts
@@ -474,13 +485,15 @@ bool literalsAreEqual(Literal lhs, Literal rhs) {
} }
return true; return true;
//NOTE: any covered by check at the top of the function case LITERAL_ANY:
return true;
default: case LITERAL_FUNCTION_INTERMEDIATE:
//should never bee seen fprintf(stderr, ERROR "[internal] Can't compare functions\n" RESET);
fprintf(stderr, ERROR "[internal] Unrecognized literal type in equality: %d\n" RESET, lhs.type);
return false; return false;
} }
return false;
} }
int hashLiteral(Literal lit) { int hashLiteral(Literal lit) {

View File

@@ -12,12 +12,14 @@ typedef enum {
LITERAL_STRING, LITERAL_STRING,
LITERAL_ARRAY, LITERAL_ARRAY,
LITERAL_DICTIONARY, LITERAL_DICTIONARY,
LITERAL_FUNCTION, //TODO: to be implemented later; the type is still handled for the most part LITERAL_FUNCTION,
//these are meta-level types //these are meta-level types
LITERAL_IDENTIFIER, LITERAL_IDENTIFIER,
LITERAL_TYPE, LITERAL_TYPE,
LITERAL_TYPE_INTERMEDIATE, //used to process types in the compiler only LITERAL_TYPE_INTERMEDIATE, //used to process types in the compiler only
LITERAL_FUNCTION_INTERMEDIATE, //used to process functions in the compiler only
// LITERAL_FUNCTION_NATIVE, //for handling native functions
LITERAL_ANY, //used by the type system only LITERAL_ANY, //used by the type system only
} LiteralType; } LiteralType;
@@ -35,7 +37,10 @@ typedef struct {
void* array; void* array;
void* dictionary; void* dictionary;
// void* function; struct {
void* ptr;
int length;
} function;
struct { //for variable names struct { //for variable names
char* ptr; char* ptr;
@@ -70,7 +75,7 @@ typedef struct {
#define AS_STRING(value) ((value).as.string.ptr) #define AS_STRING(value) ((value).as.string.ptr)
#define AS_ARRAY(value) ((LiteralArray*)((value).as.array)) #define AS_ARRAY(value) ((LiteralArray*)((value).as.array))
#define AS_DICTIONARY(value) ((LiteralDictionary*)((value).as.dictionary)) #define AS_DICTIONARY(value) ((LiteralDictionary*)((value).as.dictionary))
// #define AS_FUNCTION(value) #define AS_FUNCTION(value) ((value).as.function.ptr)
#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)
@@ -81,7 +86,7 @@ typedef struct {
#define TO_STRING_LITERAL(value) _toStringLiteral(value) #define TO_STRING_LITERAL(value) _toStringLiteral(value)
#define TO_ARRAY_LITERAL(value) ((Literal){LITERAL_ARRAY, { .array = value }}) #define TO_ARRAY_LITERAL(value) ((Literal){LITERAL_ARRAY, { .array = value }})
#define TO_DICTIONARY_LITERAL(value) ((Literal){LITERAL_DICTIONARY, { .dictionary = value }}) #define TO_DICTIONARY_LITERAL(value) ((Literal){LITERAL_DICTIONARY, { .dictionary = value }})
// #define TO_FUNCTION_LITERAL #define TO_FUNCTION_LITERAL(value, l) ((Literal){LITERAL_FUNCTION, { .function.ptr = value, .function.length = l }})
#define TO_IDENTIFIER_LITERAL(value) _toIdentifierLiteral(value, strlen(value)) #define TO_IDENTIFIER_LITERAL(value) _toIdentifierLiteral(value, strlen(value))
#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 }})

View File

@@ -62,11 +62,26 @@ void freeNode(Node* node) {
freeNode(node->varDecl.expression); freeNode(node->varDecl.expression);
break; break;
case NODE_FN_DECL:
freeLiteral(node->fnDecl.identifier);
freeNode(node->fnDecl.arguments);
freeNode(node->fnDecl.returns);
freeNode(node->fnDecl.block);
break;
case NODE_FN_COLLECTION:
for (int i = 0; i < node->fnCollection.count; i++) {
freeNode(node->fnCollection.nodes + i);
}
FREE_ARRAY(Node, node->fnCollection.nodes, node->fnCollection.capacity);
break;
case NODE_PATH_IF: case NODE_PATH_IF:
case NODE_PATH_WHILE: case NODE_PATH_WHILE:
case NODE_PATH_FOR: case NODE_PATH_FOR:
case NODE_PATH_BREAK: case NODE_PATH_BREAK:
case NODE_PATH_CONTINUE: case NODE_PATH_CONTINUE:
case NODE_PATH_RETURN:
freeNode(node->path.preClause); freeNode(node->path.preClause);
freeNode(node->path.postClause); freeNode(node->path.postClause);
freeNode(node->path.condition); freeNode(node->path.condition);
@@ -171,6 +186,29 @@ void emitNodeVarDecl(Node** nodeHandle, Literal identifier, Literal type, Node*
*nodeHandle = tmp; *nodeHandle = tmp;
} }
void emitNodeFnDecl(Node** nodeHandle, Literal identifier, Node* arguments, Node* returns, Node* block) {
Node* tmp = ALLOCATE(Node, 1);
tmp->type = NODE_FN_DECL;
tmp->fnDecl.identifier = identifier;
tmp->fnDecl.arguments = arguments;
tmp->fnDecl.returns = returns;
tmp->fnDecl.block = block;
*nodeHandle = tmp;
}
void emitNodeFnCollection(Node** nodeHandle) {
Node* tmp = ALLOCATE(Node, 1);
tmp->type = NODE_FN_COLLECTION;
tmp->fnCollection.nodes = NULL;
tmp->fnCollection.capacity = 0;
tmp->fnCollection.count = 0;
*nodeHandle = tmp;
}
void emitNodePath(Node** nodeHandle, NodeType type, Node* preClause, Node* postClause, Node* condition, Node* thenPath, Node* elsePath) { void emitNodePath(Node** nodeHandle, NodeType type, Node* preClause, Node* postClause, Node* condition, Node* thenPath, Node* elsePath) {
Node* tmp = ALLOCATE(Node, 1); Node* tmp = ALLOCATE(Node, 1);

View File

@@ -18,11 +18,14 @@ typedef enum NodeType {
NODE_PAIR, //contains a left and right NODE_PAIR, //contains a left and right
NODE_VAR_TYPES, //contains a type and a sub-node array for compound types NODE_VAR_TYPES, //contains a type and a sub-node array for compound types
NODE_VAR_DECL, //contains identifier literal, typenode, expression definition NODE_VAR_DECL, //contains identifier literal, typenode, expression definition
NODE_FN_DECL, //containd identifier literal, arguments node, returns node, block node
NODE_FN_COLLECTION, //parts of a function
NODE_PATH_IF, //for control flow NODE_PATH_IF, //for control flow
NODE_PATH_WHILE, //for control flow NODE_PATH_WHILE, //for control flow
NODE_PATH_FOR, //for control flow NODE_PATH_FOR, //for control flow
NODE_PATH_BREAK, NODE_PATH_BREAK,
NODE_PATH_CONTINUE, NODE_PATH_CONTINUE,
NODE_PATH_RETURN,
NODE_INCREMENT_PREFIX, NODE_INCREMENT_PREFIX,
NODE_INCREMENT_POSTFIX, NODE_INCREMENT_POSTFIX,
} NodeType; } NodeType;
@@ -83,6 +86,21 @@ typedef struct NodeVarDecl {
Node* expression; Node* expression;
} NodeVarDecl; } NodeVarDecl;
typedef struct NodeFnDecl {
NodeType type;
Literal identifier;
Node* arguments;
Node* returns;
Node* block;
} NodeFnDecl;
typedef struct NodeFnCollection {
NodeType type;
Node* nodes;
int capacity;
int count;
} NodeFnCollection;
typedef struct NodePath { typedef struct NodePath {
NodeType type; NodeType type;
Node* preClause; Node* preClause;
@@ -109,6 +127,8 @@ union _node {
NodePair pair; NodePair pair;
NodeVarTypes varTypes; NodeVarTypes varTypes;
NodeVarDecl varDecl; NodeVarDecl varDecl;
NodeFnDecl fnDecl;
NodeFnCollection fnCollection;
NodePath path; NodePath path;
NodeIncrement increment; NodeIncrement increment;
}; };
@@ -123,6 +143,8 @@ void emitNodeCompound(Node** nodeHandle, LiteralType literalType);
void emitNodePair(Node** nodeHandle, Node* left, Node* right); void emitNodePair(Node** nodeHandle, Node* left, Node* right);
void emitNodeVarTypes(Node** nodeHandle, Literal literal); void emitNodeVarTypes(Node** nodeHandle, Literal literal);
void emitNodeVarDecl(Node** nodeHandle, Literal identifier, Literal type, Node* expression); void emitNodeVarDecl(Node** nodeHandle, Literal identifier, Literal type, Node* expression);
void emitNodeFnDecl(Node** nodeHandle, Literal identifier, Node* arguments, Node* returns, Node* block);
void emitNodeFnCollection(Node** nodeHandle);
void emitNodePath(Node** nodeHandle, NodeType type, Node* preClause, Node* postClause, Node* condition, Node* thenPath, Node* elsePath); void emitNodePath(Node** nodeHandle, NodeType type, Node* preClause, Node* postClause, Node* condition, Node* thenPath, Node* elsePath);
void emiteNodePrefixIncrement(Node** nodeHandle, Literal identifier, int increment); void emiteNodePrefixIncrement(Node** nodeHandle, Literal identifier, int increment);
void emiteNodePostfixIncrement(Node** nodeHandle, Literal identifier, int increment); void emiteNodePostfixIncrement(Node** nodeHandle, Literal identifier, int increment);

View File

@@ -32,6 +32,9 @@ typedef enum Opcode {
OP_VAR_DECL, //declare a variable to be used (as a literal) OP_VAR_DECL, //declare a variable to be used (as a literal)
OP_VAR_DECL_LONG, //declare a variable to be used (as a long literal) OP_VAR_DECL_LONG, //declare a variable to be used (as a long literal)
OP_FN_DECL, //declare a function to be used (as a literal)
OP_FN_DECL_LONG, //declare a function to be used (as a long literal)
OP_VAR_ASSIGN, //assign to a literal OP_VAR_ASSIGN, //assign to a literal
OP_VAR_ADDITION_ASSIGN, OP_VAR_ADDITION_ASSIGN,
OP_VAR_SUBTRACTION_ASSIGN, OP_VAR_SUBTRACTION_ASSIGN,
@@ -57,9 +60,11 @@ typedef enum Opcode {
//jumps, and conditional jumps (absolute) //jumps, and conditional jumps (absolute)
OP_JUMP, OP_JUMP,
OP_IF_FALSE_JUMP, OP_IF_FALSE_JUMP,
OP_RETURN,
//meta //meta
OP_SECTION_END, OP_FN_END, //different from SECTION_END
OP_SECTION_END = 255,
//TODO: add more //TODO: add more
} Opcode; } Opcode;

View File

@@ -1094,6 +1094,33 @@ static void continueStmt(Parser* parser, Node** nodeHandle) {
consume(parser, TOKEN_SEMICOLON, "Expected ';' at end of continue statement"); consume(parser, TOKEN_SEMICOLON, "Expected ';' at end of continue statement");
} }
static void returnStmt(Parser* parser, Node** nodeHandle) {
Node* returnValues = NULL;
emitNodeFnCollection(&returnValues);
if (!match(parser, TOKEN_SEMICOLON)) {
do {
//append the node to the return list (grow the node if needed)
if (returnValues->fnCollection.capacity < returnValues->fnCollection.count + 1) {
int oldCapacity = returnValues->fnCollection.capacity;
returnValues->fnCollection.capacity = GROW_CAPACITY(oldCapacity);
returnValues->fnCollection.nodes = GROW_ARRAY(Node, returnValues->fnCollection.nodes, oldCapacity, returnValues->fnCollection.capacity);
}
Node* node = NULL;
parsePrecedence(parser, &node, PREC_TERNARY);
returnValues->fnCollection.nodes[returnValues->fnCollection.count++] = *node;
} while(match(parser, TOKEN_COMMA));
consume(parser, TOKEN_SEMICOLON, "Expected ';' at end of return statement");
}
freeNode(*nodeHandle); //free the initial node
emitNodePath(nodeHandle, NODE_PATH_RETURN, NULL, NULL, NULL, returnValues, NULL);
}
//precedence functions //precedence functions
static void expressionStmt(Parser* parser, Node** nodeHandle) { static void expressionStmt(Parser* parser, Node** nodeHandle) {
//BUGFIX: check for empty statements //BUGFIX: check for empty statements
@@ -1164,6 +1191,12 @@ static void statement(Parser* parser, Node** nodeHandle) {
return; return;
} }
//return
if (match(parser, TOKEN_RETURN)) {
returnStmt(parser, nodeHandle);
return;
}
//default //default
expressionStmt(parser, nodeHandle); expressionStmt(parser, nodeHandle);
} }
@@ -1257,7 +1290,15 @@ static void varDecl(Parser* parser, Node** nodeHandle) {
consume(parser, TOKEN_IDENTIFIER, "Expected identifier after var keyword"); consume(parser, TOKEN_IDENTIFIER, "Expected identifier after var keyword");
Token identifierToken = parser->previous; Token identifierToken = parser->previous;
char* cpy = copyString(identifierToken.lexeme, identifierToken.length); int length = identifierToken.length;
//for safety
if (length > 256) {
length = 256;
error(parser, parser->previous, "Identifiers can only be a maximum of 256 characters long");
}
char* cpy = copyString(identifierToken.lexeme, length);
Literal identifier = _toIdentifierLiteral(cpy, strlen(cpy)); //BUGFIX: use this instead of the macro Literal identifier = _toIdentifierLiteral(cpy, strlen(cpy)); //BUGFIX: use this instead of the macro
//read the type, if present //read the type, if present
@@ -1289,11 +1330,155 @@ static void varDecl(Parser* parser, Node** nodeHandle) {
consume(parser, TOKEN_SEMICOLON, "Expected ';' at end of var declaration"); consume(parser, TOKEN_SEMICOLON, "Expected ';' at end of var declaration");
} }
static void fnDecl(Parser* parser, Node** nodeHandle) {
//read the identifier
consume(parser, TOKEN_IDENTIFIER, "Expected identifier after var keyword");
Token identifierToken = parser->previous;
int length = identifierToken.length;
//for safety
if (length > 256) {
length = 256;
error(parser, parser->previous, "Identifiers can only be a maximum of 256 characters long");
}
char* cpy = copyString(identifierToken.lexeme, length);
Literal identifier = _toIdentifierLiteral(cpy, strlen(cpy)); //BUGFIX: use this instead of the macro
//TODO: read the parameters and arity
consume(parser, TOKEN_PAREN_LEFT, "Expected '(' after function identifier");
//for holding the array of arguments
Node* argumentNode = NULL;
emitNodeFnCollection(&argumentNode);
//read args
if (!match(parser, TOKEN_PAREN_RIGHT)) {
do {
//check for rest parameter
if (match(parser, TOKEN_REST)) {
//read the argument identifier
consume(parser, TOKEN_IDENTIFIER, "Expected identifier as function argument");
Token argIdentifierToken = parser->previous;
int length = argIdentifierToken.length;
//for safety
if (length > 256) {
length = 256;
error(parser, parser->previous, "Identifiers can only be a maximum of 256 characters long");
}
char* cpy = copyString(argIdentifierToken.lexeme, length);
Literal argIdentifier = _toIdentifierLiteral(cpy, strlen(cpy)); //BUGFIX: use this instead of the macro
//set the type (array of any types)
Literal argTypeLiteral = TO_TYPE_LITERAL(LITERAL_ARRAY, false);
TYPE_PUSH_SUBTYPE(&argTypeLiteral, TO_TYPE_LITERAL(LITERAL_ANY, false));
//emit the node to the argument list (grow the node if needed)
if (argumentNode->fnCollection.capacity < argumentNode->fnCollection.count + 1) {
int oldCapacity = argumentNode->fnCollection.capacity;
argumentNode->fnCollection.capacity = GROW_CAPACITY(oldCapacity);
argumentNode->fnCollection.nodes = GROW_ARRAY(Node, argumentNode->fnCollection.nodes, oldCapacity, argumentNode->fnCollection.capacity);
}
//store the arg in the array
Node* literalNode = NULL;
emitNodeVarDecl(&literalNode, argIdentifier, argTypeLiteral, NULL);
argumentNode->fnCollection.nodes[argumentNode->fnCollection.count++] = *literalNode;
break;
}
//read the argument identifier
consume(parser, TOKEN_IDENTIFIER, "Expected identifier as function argument");
Token argIdentifierToken = parser->previous;
int length = argIdentifierToken.length;
//for safety
if (length > 256) {
length = 256;
error(parser, parser->previous, "Identifiers can only be a maximum of 256 characters long");
}
char* cpy = copyString(argIdentifierToken.lexeme, length);
Literal argIdentifier = _toIdentifierLiteral(cpy, strlen(cpy)); //BUGFIX: use this instead of the macro
//read optional type of the identifier
Literal argTypeLiteral;
if (match(parser, TOKEN_COLON)) {
argTypeLiteral = readTypeToLiteral(parser);
}
else {
//default to non-const any
argTypeLiteral = TO_TYPE_LITERAL(LITERAL_ANY, false);
}
//emit the node to the argument list (grow the node if needed)
if (argumentNode->fnCollection.capacity < argumentNode->fnCollection.count + 1) {
int oldCapacity = argumentNode->fnCollection.capacity;
argumentNode->fnCollection.capacity = GROW_CAPACITY(oldCapacity);
argumentNode->fnCollection.nodes = GROW_ARRAY(Node, argumentNode->fnCollection.nodes, oldCapacity, argumentNode->fnCollection.capacity);
}
//store the arg in the array
Node* literalNode = NULL;
emitNodeVarDecl(&literalNode, argIdentifier, argTypeLiteral, NULL);
argumentNode->fnCollection.nodes[argumentNode->fnCollection.count++] = *literalNode;
} while (match(parser, TOKEN_COMMA)); //if comma is read, continue
consume(parser, TOKEN_PAREN_RIGHT, "Expected ')' after function argument list");
}
//read the return types, if present
Node* returnNode = NULL;
emitNodeFnCollection(&returnNode);
if (match(parser, TOKEN_COLON)) {
do {
//append the node to the return list (grow the node if needed)
if (returnNode->fnCollection.capacity < returnNode->fnCollection.count + 1) {
int oldCapacity = returnNode->fnCollection.capacity;
returnNode->fnCollection.capacity = GROW_CAPACITY(oldCapacity);
returnNode->fnCollection.nodes = GROW_ARRAY(Node, returnNode->fnCollection.nodes, oldCapacity, returnNode->fnCollection.capacity);
}
Node* literalNode = NULL;
emitNodeLiteral(&literalNode, readTypeToLiteral(parser));
returnNode->fnCollection.nodes[returnNode->fnCollection.count++] = *literalNode;
} while(match(parser, TOKEN_COMMA));
}
//read the function body
consume(parser, TOKEN_BRACE_LEFT, "Expected '{' after return list");
Node* blockNode = ALLOCATE(Node, 1);
blockStmt(parser, &blockNode);
//declare it
freeNode(*nodeHandle); //free the initial node, because WTF?
emitNodeFnDecl(nodeHandle, identifier, argumentNode, returnNode, blockNode);
}
static void declaration(Parser* parser, Node** nodeHandle) { //assume nodeHandle holds a blank node static void declaration(Parser* parser, Node** nodeHandle) { //assume nodeHandle holds a blank node
//variable declarations //variable declarations
if (match(parser, TOKEN_VAR)) { if (match(parser, TOKEN_VAR)) {
varDecl(parser, nodeHandle); varDecl(parser, nodeHandle);
} }
else if (match(parser, TOKEN_FUNCTION)) {
fnDecl(parser, nodeHandle);
}
else { else {
statement(parser, nodeHandle); statement(parser, nodeHandle);
} }

View File

@@ -124,6 +124,12 @@ void runSource(char* source) {
if (!tb) { if (!tb) {
return; return;
} }
//DEBUG
// for (size_t i = 0; i < size; i++) {
// printf("%d, ", tb[i]);
// }
runBinary(tb, size); runBinary(tb, size);
} }