diff --git a/docs/TODO.txt b/docs/TODO.txt index 1a28086..3efb00c 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -25,6 +25,7 @@ TODO: functions are invoked by calling thier names TODO: functions are first-class citizens 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 = b = c = 1; ? TODO: are compounds shallow or deep copies? diff --git a/scripts/function.toy b/scripts/function.toy new file mode 100644 index 0000000..0111790 --- /dev/null +++ b/scripts/function.toy @@ -0,0 +1,9 @@ + + +fn name(argument: type, arg2: type, ...rest): int { + print "hello world"; + return 0; +} + + +print name; \ No newline at end of file diff --git a/source/compiler.c b/source/compiler.c index fcaa6bd..fadb01c 100644 --- a/source/compiler.c +++ b/source/compiler.c @@ -21,6 +21,36 @@ void initCompiler(Compiler* compiler) { } //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) { int index = -1; @@ -124,34 +154,61 @@ static int writeNodeCompoundToCache(Compiler* compiler, Node* node) { return index; } -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); +static int writeNodeCollectionToCache(Compiler* compiler, Node* node) { + //stored as an array + LiteralArray* store = ALLOCATE(LiteralArray, 1); + initLiteralArray(store); - //store the base literal in the store - pushLiteralArray(store, literal); + //ensure each literal value is in the cache, individually + 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++) { - //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)); + int typeIndex = writeLiteralTypeToCache(&compiler->literalCache, node->fnCollection.nodes[i].varDecl.typeLiteral); + + //embed the info into the bytecode + 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 - int index = findLiteralIndex(literalCache, literal); - if (index < 0) { - index = pushLiteralArray(literalCache, literal); - } - - return index; + //push the store to the cache, with instructions about how pack it + return pushLiteralArray(&compiler->literalCache, TO_ARRAY_LITERAL(store)); } static int writeLiteralToCompiler(Compiler* compiler, Literal literal) { @@ -310,6 +367,66 @@ static void writeCompilerWithJumps(Compiler* compiler, Node* node, void* breakAd } 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: { //process the condition writeCompilerWithJumps(compiler, node->path.condition, breakAddressesPtr, continueAddressesPtr); @@ -481,6 +598,20 @@ static void writeCompilerWithJumps(Compiler* compiler, Node* node, void* breakAd } 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: { //push the literal to the stack (twice) writeLiteralToCompiler(compiler, node->increment.identifier); @@ -582,29 +713,37 @@ static void emitFloat(unsigned char** collationPtr, int* capacityPtr, int* count } //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 count = 0; unsigned char* collation = ALLOCATE(unsigned char, capacity); - //embed the header with version information - emitByte(&collation, &capacity, &count, TOY_VERSION_MAJOR); - emitByte(&collation, &capacity, &count, TOY_VERSION_MINOR); - emitByte(&collation, &capacity, &count, TOY_VERSION_PATCH); + //for the function-section at the end of the main-collation + int fnIndex = 0; //counts up for each fn + int fnCapacity = GROW_CAPACITY(0); + int fnCount = 0; + unsigned char* fnCollation = ALLOCATE(unsigned char, fnCapacity); - //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); + if (embedHeader) { + //embed the header with version information + emitByte(&collation, &capacity, &count, TOY_VERSION_MAJOR); + emitByte(&collation, &capacity, &count, TOY_VERSION_MINOR); + 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) emitShort(&collation, &capacity, &count, compiler->literalCache.count); @@ -684,7 +823,33 @@ unsigned char* collateCompiler(Compiler* compiler, int* size) { } 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: { 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 + //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 for (int i = 0; i < compiler->count; i++) { emitByte(&collation, &capacity, &count, compiler->bytecode[i]); @@ -759,4 +936,8 @@ unsigned char* collateCompiler(Compiler* compiler, int* size) { *size = count; return collation; -} \ No newline at end of file +} + +unsigned char* collateCompiler(Compiler* compiler, int* size) { + return collateCompilerHeaderOpt(compiler, size, true); +} diff --git a/source/interpreter.c b/source/interpreter.c index 594a44a..37f5674 100644 --- a/source/interpreter.c +++ b/source/interpreter.c @@ -44,6 +44,10 @@ void freeInterpreter(Interpreter* interpreter) { 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); @@ -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 const short literalCount = readShort(interpreter->bytecode, &interpreter->count); @@ -1017,7 +988,22 @@ void runInterpreter(Interpreter* interpreter, unsigned char* bytecode, int lengt } 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: { 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 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 if (command.verbose) { printf(NOTICE "executing bytecode\n" RESET); } + //execute the interpreter execInterpreter(interpreter); //free the bytecode immediately after use diff --git a/source/interpreter.h b/source/interpreter.h index 79b99ec..ebdc6cd 100644 --- a/source/interpreter.h +++ b/source/interpreter.h @@ -14,7 +14,7 @@ typedef struct Interpreter { unsigned char* bytecode; int length; int count; - int codeStart; + int codeStart; //for jumps LiteralArray literalCache; //read-only - built from the bytecode, refreshed each time new bytecode is provided //operation diff --git a/source/lexer.c b/source/lexer.c index 20c1bb0..6105f24 100644 --- a/source/lexer.c +++ b/source/lexer.c @@ -284,7 +284,9 @@ Token scanLexer(Lexer* lexer) { case ';': return makeToken(lexer, TOKEN_SEMICOLON); case ',': return makeToken(lexer, TOKEN_COMMA); case '.': - if (peek(lexer) == '.' && peekNext(lexer) == ',') { + if (peek(lexer) == '.' && peekNext(lexer) == '.') { + advance(lexer); + advance(lexer); return makeToken(lexer, TOKEN_REST); } return makeToken(lexer, TOKEN_DOT); diff --git a/source/literal.c b/source/literal.c index 4885c4e..fa9a777 100644 --- a/source/literal.c +++ b/source/literal.c @@ -187,6 +187,10 @@ void printLiteralCustom(Literal literal, void (printFn)(const char*)) { break; //TODO: functions + case LITERAL_FUNCTION: { + printFn("(function)"); + } + break; case LITERAL_IDENTIFIER: { char buffer[256]; @@ -293,9 +297,14 @@ void printLiteralCustom(Literal literal, void (printFn)(const char*)) { } break; - default: - //should never bee seen - fprintf(stderr, ERROR "[internal] Unrecognized literal type in print: %d\n" RESET, literal.type); + case LITERAL_TYPE_INTERMEDIATE: + case LITERAL_FUNCTION_INTERMEDIATE: + printFn("Unprintable literal found"); + break; + + case LITERAL_ANY: + printFn("(any)"); + break; } } @@ -438,7 +447,9 @@ bool literalsAreEqual(Literal lhs, Literal rhs) { return true; - //TODO: functions + case LITERAL_FUNCTION: + return false; //functions are never equal + break; case LITERAL_IDENTIFIER: //check shortcuts @@ -474,13 +485,15 @@ bool literalsAreEqual(Literal lhs, Literal rhs) { } return true; - //NOTE: any covered by check at the top of the function + case LITERAL_ANY: + return true; - default: - //should never bee seen - fprintf(stderr, ERROR "[internal] Unrecognized literal type in equality: %d\n" RESET, lhs.type); + case LITERAL_FUNCTION_INTERMEDIATE: + fprintf(stderr, ERROR "[internal] Can't compare functions\n" RESET); return false; } + + return false; } int hashLiteral(Literal lit) { diff --git a/source/literal.h b/source/literal.h index 61241a0..0268677 100644 --- a/source/literal.h +++ b/source/literal.h @@ -12,12 +12,14 @@ typedef enum { LITERAL_STRING, LITERAL_ARRAY, 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 LITERAL_IDENTIFIER, LITERAL_TYPE, 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 } LiteralType; @@ -35,7 +37,10 @@ typedef struct { void* array; void* dictionary; - // void* function; + struct { + void* ptr; + int length; + } function; struct { //for variable names char* ptr; @@ -70,7 +75,7 @@ typedef struct { #define AS_STRING(value) ((value).as.string.ptr) #define AS_ARRAY(value) ((LiteralArray*)((value).as.array)) #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_TYPE(value) ((value).as.type) @@ -81,7 +86,7 @@ typedef struct { #define TO_STRING_LITERAL(value) _toStringLiteral(value) #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_FUNCTION_LITERAL(value, l) ((Literal){LITERAL_FUNCTION, { .function.ptr = value, .function.length = l }}) #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 }}) diff --git a/source/node.c b/source/node.c index 5322c8c..bb20192 100644 --- a/source/node.c +++ b/source/node.c @@ -62,11 +62,26 @@ void freeNode(Node* node) { freeNode(node->varDecl.expression); 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_WHILE: case NODE_PATH_FOR: case NODE_PATH_BREAK: case NODE_PATH_CONTINUE: + case NODE_PATH_RETURN: freeNode(node->path.preClause); freeNode(node->path.postClause); freeNode(node->path.condition); @@ -171,6 +186,29 @@ void emitNodeVarDecl(Node** nodeHandle, Literal identifier, Literal type, Node* *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) { Node* tmp = ALLOCATE(Node, 1); diff --git a/source/node.h b/source/node.h index 4a140ba..fa4d3b6 100644 --- a/source/node.h +++ b/source/node.h @@ -18,11 +18,14 @@ typedef enum NodeType { NODE_PAIR, //contains a left and right NODE_VAR_TYPES, //contains a type and a sub-node array for compound types 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_WHILE, //for control flow NODE_PATH_FOR, //for control flow NODE_PATH_BREAK, NODE_PATH_CONTINUE, + NODE_PATH_RETURN, NODE_INCREMENT_PREFIX, NODE_INCREMENT_POSTFIX, } NodeType; @@ -83,6 +86,21 @@ typedef struct NodeVarDecl { Node* expression; } 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 { NodeType type; Node* preClause; @@ -109,6 +127,8 @@ union _node { NodePair pair; NodeVarTypes varTypes; NodeVarDecl varDecl; + NodeFnDecl fnDecl; + NodeFnCollection fnCollection; NodePath path; NodeIncrement increment; }; @@ -123,6 +143,8 @@ void emitNodeCompound(Node** nodeHandle, LiteralType literalType); void emitNodePair(Node** nodeHandle, Node* left, Node* right); void emitNodeVarTypes(Node** nodeHandle, Literal literal); 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 emiteNodePrefixIncrement(Node** nodeHandle, Literal identifier, int increment); void emiteNodePostfixIncrement(Node** nodeHandle, Literal identifier, int increment); diff --git a/source/opcodes.h b/source/opcodes.h index ee8e71c..7f1a1cc 100644 --- a/source/opcodes.h +++ b/source/opcodes.h @@ -32,6 +32,9 @@ typedef enum Opcode { 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_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_ADDITION_ASSIGN, OP_VAR_SUBTRACTION_ASSIGN, @@ -57,9 +60,11 @@ typedef enum Opcode { //jumps, and conditional jumps (absolute) OP_JUMP, OP_IF_FALSE_JUMP, + OP_RETURN, //meta - OP_SECTION_END, + OP_FN_END, //different from SECTION_END + OP_SECTION_END = 255, //TODO: add more } Opcode; diff --git a/source/parser.c b/source/parser.c index fffde09..49e6027 100644 --- a/source/parser.c +++ b/source/parser.c @@ -1094,6 +1094,33 @@ static void continueStmt(Parser* parser, Node** nodeHandle) { 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 static void expressionStmt(Parser* parser, Node** nodeHandle) { //BUGFIX: check for empty statements @@ -1164,6 +1191,12 @@ static void statement(Parser* parser, Node** nodeHandle) { return; } + //return + if (match(parser, TOKEN_RETURN)) { + returnStmt(parser, nodeHandle); + return; + } + //default expressionStmt(parser, nodeHandle); } @@ -1257,7 +1290,15 @@ static void varDecl(Parser* parser, Node** nodeHandle) { consume(parser, TOKEN_IDENTIFIER, "Expected identifier after var keyword"); 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 //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"); } +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 //variable declarations if (match(parser, TOKEN_VAR)) { varDecl(parser, nodeHandle); } + else if (match(parser, TOKEN_FUNCTION)) { + fnDecl(parser, nodeHandle); + } else { statement(parser, nodeHandle); } diff --git a/source/repl_main.c b/source/repl_main.c index 4e077ce..8969ee1 100644 --- a/source/repl_main.c +++ b/source/repl_main.c @@ -124,6 +124,12 @@ void runSource(char* source) { if (!tb) { return; } + + //DEBUG + // for (size_t i = 0; i < size; i++) { + // printf("%d, ", tb[i]); + // } + runBinary(tb, size); }