mirror of
https://github.com/krgamestudios/Toy.git
synced 2026-04-15 23:04:08 +10:00
Function declaration seems to work
This commit is contained in:
@@ -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
9
scripts/function.toy
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
|
||||||
|
fn name(argument: type, arg2: type, ...rest): int {
|
||||||
|
print "hello world";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
print name;
|
||||||
@@ -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]);
|
||||||
@@ -759,4 +936,8 @@ unsigned char* collateCompiler(Compiler* compiler, int* size) {
|
|||||||
*size = count;
|
*size = count;
|
||||||
|
|
||||||
return collation;
|
return collation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned char* collateCompiler(Compiler* compiler, int* size) {
|
||||||
|
return collateCompilerHeaderOpt(compiler, size, true);
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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 }})
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
187
source/parser.c
187
source/parser.c
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user